using System;
using System.Collections.Generic;
using System.Text;

namespace VisualGTest.Binding
{
    /// <summary>
    /// A dynamically resizing array of bytes.
    /// </summary>
    class ByteBuffer
    {
        /// <summary>
        /// The capacity of a single internal buffer. The array is represented
        /// by one or more of these buffers internally.
        /// </summary>
        public static readonly int BUFFER_CAPACITY = 4096;

        private List<byte[]> m_buffers;
        private int m_bufferIndex;
        private int m_bufferPos;
        private int m_bufferSize;

        public ByteBuffer()
        {
            m_buffers = new List<byte[]>();
            m_bufferIndex = 0;
            m_bufferPos = 0;
            m_bufferSize = 0;
        }

        /// <summary>
        /// A copy of the bytes this buffer represents, sized precisely for
        /// the content it contains.
        /// </summary>
        public byte[] Bytes
        {
            get
            {
                byte[] bytes = new byte[m_bufferSize];

                for (int i = 0; i <= m_bufferIndex; ++i)
                {
                    int offset = BUFFER_CAPACITY * i;
                    int length = Math.Min(m_bufferSize - offset, BUFFER_CAPACITY);
                    Array.Copy(m_buffers[i], 0, bytes, offset, length);
                }

                return bytes;
            }
        }

        /// <summary>
        /// Appends a copy of the given byte array to the buffer.
        /// </summary>
        /// <param name="bytes">the bytes to append</param>
        /// <returns></returns>
        public ByteBuffer Append(byte[] bytes)
        {
            return Append(bytes, bytes.Length);
        }

        /// <summary>
        /// Appends a copy of the given byte array to the buffer.
        /// </summary>
        /// <param name="bytes">the bytes to append</param>
        /// <param name="length">the number of bytes to append</param>
        /// <returns></returns>
        public ByteBuffer Append(byte[] bytes, int length)
        {
            int sourceIndex = 0;

            while (sourceIndex < length)
            {
                EnsureBufferAvailable(m_bufferIndex);

                int availableCapacity = BUFFER_CAPACITY - m_bufferPos;
                int bytesToCopy = Math.Min(length - sourceIndex, availableCapacity);

                Array.Copy(bytes, sourceIndex, m_buffers[m_bufferIndex], m_bufferPos, bytesToCopy);

                sourceIndex += bytesToCopy;
                m_bufferPos += bytesToCopy;
                m_bufferSize += bytesToCopy;

                if (m_bufferPos == BUFFER_CAPACITY)
                {
                    ++m_bufferIndex;
                    m_bufferPos = 0;
                }
            }

            return this;
        }

        /// <summary>
        /// Clears the data from internal buffers. The buffers themselves
        /// remain allocated.
        /// </summary>
        public void Clear()
        {
            m_bufferIndex = 0;
            m_bufferPos = 0;
            m_bufferSize = 0;
        }

        /// <summary>
        /// Ensures that m_buffers can be indexed with the given index, by
        /// appending a new buffer if necessary.
        /// </summary>
        private void EnsureBufferAvailable(int bufferIndex)
        {
            while (m_buffers.Count <= bufferIndex)
            {
                m_buffers.Add(new byte[BUFFER_CAPACITY]);
            }
        }


    }
}
